#include "nv_l40_gpu.h"
#include "mc/log.h"
#include <thread>

namespace dev {

bool nv_l40_gpu::start() {
    elog("nv_l40_gpu start.");
    return true;
}

bool nv_l40_gpu::stop() {
    elog("nv_l40_gpu stop.");
    if (m_postbox_timer && m_postbox_timer->is_active()) {
        m_postbox_timer->stop();
    }
    m_postbox_timer = nullptr;
    return true;
}

bool nv_l40_gpu::init(mc::mutable_dict& config, const mc::dict& connector) {
    elog("nv_l40_gpu init.");
    try {
        from_variant(config, *this);

        // 初始化 smbus_postbox 协议
        if (!init_smbus_postbox()) {
            elog("SMBusPostbox Warning: SMBusPostbox initialization failed, continuing without "
                 "it");
        }

        /*start task 防止协议冲突 将pcie信息任务丢入此处处理*/
        mc::milliseconds interval(5000);
        m_postbox_timer = new mc::core::timer(this);
        m_postbox_timer->timeout.connect([this]() {
            m_gpu_power.read_gpu_power_consumption(m_smbus_postbox);
            m_gpu_power.read_gpu_powerbrakeIsSet(m_smbus_postbox);

            m_gpu_status.read_ECCModeEnabled(m_smbus_postbox);
            m_gpu_status.read_ECCModePendingEnabled(m_smbus_postbox);
            m_gpu_status.read_resetRequired(m_smbus_postbox);

            m_gpu.read_gpu_temperature(m_smbus_postbox);
            /*静态数据类初始化时只读一次*/
            if (!_card_builddate) {
                _card_builddate = m_gpu.read_card_builddate(m_smbus_postbox);
            }
            if (!_infoRomVersion) {
                _infoRomVersion = m_gpu.read_infoRomVersion(m_smbus_postbox);
            }
            if (!_uuid) {
                _uuid = m_gpu.read_uuid(m_smbus_postbox);
            }

            if (!_gpu_partnumber) {
                _gpu_partnumber = m_processor.read_gpu_partnumber(m_smbus_postbox);
            }

            if (!_pcie_firmware_version) {
                _pcie_firmware_version = m_pcie_card.read_firmware_version(m_smbus_postbox);
            }
            // PCIe 卡的序列号/部件号改由 PCIeCard 读取
            if (!_pcie_serialnumber) {
                _pcie_serialnumber = m_pcie_card.read_card_serialnumber(m_smbus_postbox);
            }
            if (!_pcie_partnumber) {
                _pcie_partnumber = m_pcie_card.read_card_partnumber(m_smbus_postbox);
            }
        });

        m_postbox_timer->set_single_shot(false);
        m_postbox_timer->start(interval);

    } catch (const std::exception& e) {
        elog("SMBusPostbox nv_l40_gpu init failed, exception: ${exception}",
             ("exception", e.what()));
        return false;
    }
    return true;
}

bool nv_l40_gpu::reset_default_properties() {
    m_processor.Presence                = false;
    m_processor.FirmwareVersion         = "";
    m_processor.SerialNumber            = "";
    m_processor.PartNumber              = "";
    m_gpu.InfoRomVersion                = "";
    m_gpu.BuildDate                     = "";
    m_gpu.UUID                          = "";
    m_gpu.Utilization                   = 0;
    m_gpu.TemperatureCelsius            = 0;
    m_gpu_power.PowerWatts              = 0;
    m_gpu_power.PowerBrakeState         = 255;
    m_gpu_power.ExternalPowerSufficient = 255;
    m_gpu_status.ECCModeEnabled         = 255;
    m_gpu_status.ECCModePendingEnabled  = 255;
    m_gpu_status.ResetRequired          = 255;

    return true;
}

void nv_l40_gpu::register_ref_chip() {
    // m_ref_chip = ref_chip;
    auto ref_chip_value = m_gpu.RefChip.get_value();
    if (ref_chip_value.is_extension()) {
        auto ref_obj = ref_chip_value.as<mc::engine::ref_object*>();
        if (ref_obj != nullptr) {
            m_ref_chip = mc::make_shared<mc::engine::ref_object>(*ref_obj);
        }
    }
}

bool nv_l40_gpu::init_smbus_postbox() {
    try {
        register_ref_chip();
        if (m_ref_chip == nullptr) {
            elog("SMBusPostbox Error: RefChip is not registered, cannot initialize SMBus");
            return false;
        }

        // 2. 创建 smbus 对象
        m_smbus_obj = mc::make_shared<smbus>(this);

        // 3. 从 RefChip 获取 MCU 地址并初始化 smbus 对象
        auto mcu_address_val = m_ref_chip->get_property("Address");
        if (mcu_address_val.is_null()) {
            elog("Error: Could not get Address from RefChip");
            return false;
        }
        uint8_t          mcu_address = mcu_address_val.as<uint8_t>();
        mc::mutable_dict params_template{{"BufferLen", 64}, {"McuAddress", mcu_address}};
        m_smbus_obj->init(params_template);
        // 4. 注册 WriteRead/Write/Read 方法
        m_smbus_obj->register_WriteRead_method([&](const std::vector<uint8_t>& data, uint32_t len) {
            auto result = m_ref_chip->invoke("bmc.dev.Chip", "BlockIOWriteRead", {data, len});
            if (result.is_null()) {
                elog("Error: BlockIOWriteRead failed");
                return std::vector<uint8_t>();
            }
            return result.as<std::vector<uint8_t>>();
        });
        m_smbus_obj->register_Write_method([&](uint32_t offset, const std::vector<uint8_t>& data) {
            auto result = m_ref_chip->invoke("bmc.dev.Chip", "BlockIOWrite", {offset, data});
            if (result.is_null()) {
                elog("Error: Write failed, offset=0x${offset:02x}", ("offset", offset));
                return std::vector<uint8_t>();
            }
            return result.as<std::vector<uint8_t>>();
        });
        m_smbus_obj->register_Read_method([&](uint32_t offset, uint32_t length) {
            auto result = m_ref_chip->invoke("bmc.dev.Chip", "BlockIORead", {offset, length});
            if (result.is_null()) {
                elog("Error: Read failed, offset=0x${offset:02x}, len=${len}",
                     ("offset", offset)("len", length));
                return std::vector<uint8_t>();
            }
            return result.as<std::vector<uint8_t>>();
        });

        // 5. 创建 smbus_postbox 对象
        m_smbus_postbox = SMBusPostboxProtocol::create();
        if (!m_smbus_postbox) {
            elog("SMBusPostbox Error: Failed to create SMBusPostboxProtocol");
            return false;
        }
        // 6. 初始化 smbus_postbox  data_reg_addr = 0x5d;
        m_smbus_postbox->init(0x5d);

        // 7. 注册 smbus 到 smbus_postbox
        m_smbus_postbox->registerSMBus(m_smbus_obj);

        elog("SMBusPostbox initialized successfully: buffer_len=${buffer_len}, "
             "mcu_address=0x${mcu_address:02x}, data_reg_addr=0x${data_reg_addr:02x}",
             ("buffer_len", 64)("mcu_address", mcu_address)("data_reg_addr", 0x5d));
        return true;
    } catch (const std::exception& e) {
        elog("SMBusPostbox init_smbus_postbox failed, exception: ${exception}",
             ("exception", e.what()));
        return false;
    }
}

} // namespace dev

MC_REFLECT(dev::nv_l40_gpu,
    ((m_processor, "bmc.dev.Processor"))((m_gpu, "bmc.dev.Gpu"))
    ((m_gpu_power, "bmc.dev.Gpu.Power"))((m_gpu_status, "bmc.dev.Gpu.Status"))((m_pcie_card, "bmc.dev.PCIeCard")))